﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using RFEngine.Entities;
using RFEngine.Graphics;
using RFEngine.Storage;

/// This is a half [censored] attempt to reclaim some control from xna. However, it still need to be expanded, and 
/// written to accept "Sound" files, along with setting a more standard for class files that relate to content loading.
/// By no means is this anywhere near complete.

namespace RFEngine.Content
{
    public sealed class ContentManager : IEntity, IDisposable
    {
        #region Fields

        private bool _IsDisposed;

        private string _Name;
        private float _TimeCreated;
        private string _FileExtension = "";
        
        private static string[] LoadableContent = new string[] 
        { 
            "ttf", "otf", "rff",            /// Font Files.
            "fx",                           /// Effect Files.
            "png", "bmp", "jpg", "jpeg",    /// Texture Files.
            "midi", "mid", "chart",         /// Note Chart Files.
            "mp3", "wav", "ogg",            /// Sound Files
            "rfs", "rfc", "scene"           /// Rockfan content files.
        };

        private EffectPool _EffectPool = new EffectPool();

        private GraphicsDevice _GraphicsDevice;

        private List<Font> _FontCollection = new List<Font>();
        private List<IDisposable> _ContentCollection = new List<IDisposable>();

        #endregion

        #region Properties

        /// <summary>
        /// Gets or Sets the name of this entity.
        /// </summary>
        public string Name
        {
            get { return _Name; }
            set { return; }
        }

        #region Interface Members

        float IEntity.TimeCreated
        {
            get { return _TimeCreated; }
        }

        #endregion

        #endregion

        public ContentManager(GraphicsDevice graphicsDevice, string name)
        {
            _GraphicsDevice = graphicsDevice;
            _Name = name;
        }

        public T Load<T>(string assetLocation) 
            where T : IDisposable
        {
            _FileExtension = StorageContainer.GetFileExtension(assetLocation);            
            if (!LoadableContent.Contains(_FileExtension))
                throw new ArgumentException("Unable to load Type of " + typeof(T).Name);

            assetLocation = StorageContainer.CurrentDirectory + assetLocation;

            #region Texture2D

            if (typeof(T) == typeof(Texture2D))
            {
                if (_FileExtension != "png" && _FileExtension != "bmp" && _FileExtension != "jpg" && _FileExtension != "jpeg")
                    throw new NotSupportedException("Error loading " +
                        assetLocation + Environment.NewLine + "Load Type does not match file type",
                        new Exception("Load Type : TEXTURE2D" + Environment.NewLine + "File Type : " + _FileExtension.ToUpper()));

                foreach (IDisposable item in _ContentCollection)
                    if (item is Microsoft.Xna.Framework.Graphics.Texture2D)
                        if ((item as Microsoft.Xna.Framework.Graphics.Texture2D).Name == assetLocation)
                            return (T)item;

                object obj = Microsoft.Xna.Framework.Graphics.Texture2D.FromFile(_GraphicsDevice, @assetLocation);
                (obj as Texture2D).Name = assetLocation;
                _ContentCollection.Add((T)obj);
                return (T)obj;
            }

            #endregion

            #region Effect

            if (typeof(T) == typeof(Effect))
            {
                if (_FileExtension != ".fx")
                    throw new NotSupportedException("Error loading " +
                        assetLocation + Environment.NewLine + "Load Type does not match file type",
                        new Exception("Load Type : EFFECT" + Environment.NewLine + "File Type : " + _FileExtension.ToUpper()));
                                
                var cEffect = Effect.CompileEffectFromFile(assetLocation, null, null, CompilerOptions.None, TargetPlatform.Windows);

                object obj = new Effect(_GraphicsDevice, cEffect.GetEffectCode(), CompilerOptions.None, _EffectPool);
                _ContentCollection.Add((T)obj);
                return (T)obj;
            }

            #endregion

            throw new ArgumentException("Unable to load Type of " + typeof(T).Name);
        }

        public Font LoadFont(string assetLocation, uint pointSize)
        {
            _FileExtension = StorageContainer.GetFileExtension(assetLocation);

            if (!LoadableContent.Contains(_FileExtension))
                throw new ArgumentException("Unable to load Type of " + _FileExtension);

            if (_FileExtension != "ttf" || _FileExtension != "otf" || _FileExtension != "rff")
                throw new NotSupportedException("Error loading " +
                    assetLocation + Environment.NewLine + "Load Type does not match file type",
                    new Exception("Load Type : FONT" + Environment.NewLine + "File Type : " + _FileExtension.ToUpper()));

            Font font = new Font(assetLocation, (int)pointSize);

            for (int i = 0; i < _FontCollection.Count; i++)
                if (font.Texture.GetHashCode() == _FontCollection[i].GetHashCode() &&
                    font.FontSize == _FontCollection[i].FontSize)
                {
                    (font as IDisposable).Dispose();
                    return _FontCollection[i];
                }

            _FontCollection.Add(font);
            _ContentCollection.Add(font);

            return font;
        }

        public void Unload()
        {
            foreach (IDisposable disposable in _ContentCollection)
            {
                disposable.Dispose();
                if (disposable is Font)
                    _FontCollection.Remove((Font)disposable);
            }
        }

        void IDisposable.Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        internal void Dispose(bool disposing)
        {
            if (!_IsDisposed)
            {
                if (true)
                {
                    Unload();
                    _ContentCollection.Clear();
                    _ContentCollection = null;
                    _Name = "";
                    _TimeCreated = 0;                     
                }

                _IsDisposed = true;
            }
        }

        #region Destructor

        ~ContentManager()
        {
            Dispose(false);
        }

        #endregion
    }
}
